[CCode(cheader_filename = "bson/bson.h")]
namespace Bson {
    unowned string get_version();
    public int get_major_version();
    public int get_micro_version();
    public int get_minor_version();
    public bool check_version (int required_major, int required_minor, int required_micro);
    [CCode(cname = "bson_strfreev")]
    public void free_strings(char **text);

    [CCode(cname = "bson_error_t", destroy_function = "", free_function = "")]
    public struct BsonError {
        public uint32 domain;
        public uint32 code;
        public string message;
        [CCode(cname = "bson_set_error")]
        public void set_error(uint32 domain, uint32 code, string format);
        [CCode(cname = "bson_strerror_r")]
        public static string string_error(int error_code, uint8[] buffer);
    }
    
    [CCode(cname = "bson_realloc_func")]
    public delegate T BsonReallocation<T>(void *memory, size_t length, T context);
    [CCode(cname = "bson_subtype_t", cprefix = "BSON_SUBTYPE_")]
    public enum BsonSubType {
        BINARY,
        FUNCTION,
        BINARY_DEPRECATED,
        UUID_DEPRECATED,
        UUID,
        MD5,
        USER,
    }
    [CCode(cname = "bson_decimal128_t")]
    public struct BsonDecimal128 {
        public uint64 low;
        public uint64 high;
    }

    [CCode(cname = "bson_iter_t")]
    public struct BsonIter{}

    [CCode(cname = "bson_context_flags_t")]
    public enum BsonContextFlags {
        NONE,
        THREAD_SAFE,
        DISABLE_HOST_CACHE,
        DISABLE_PID_CACHE,
        USE_TASK_ID,
    }

    [Compact]
    [CCode(cname = "bson_context_t", destroy_function = "bson_context_destroy")]
    public class BsonContext {
        [CCode(cname = "bson_context_new")]
        public BsonContext(BsonContextFlags flags);
        [CCode(cname = "bson_context_get_default")]
        public BsonContext.get_default();
    }

    [CCode(cname = "bson_oid_t", cprefix = "bson_oid_")]
    public struct BsonObjectId{
        public uint8 bytes[12];
        public static bool compare(BsonObjectId object_id1, BsonObjectId object_id2);
        public void copy(BsonObjectId target_object_id);
        public static bool equal(BsonObjectId object_id1, BsonObjectId object_id2);
        public time_t time {
            [CCode(cname = "bson_oid_get_time_t")]
            get;
        }
        public uint32 hash();
        public void init(BsonContext context);
        public void init_from_data(uint8 data[12]);
        public void init_from_string(uint8 data[12]);
        public void init_sequence(BsonContext? context = null);
        public static bool is_valid(string text, ulong text_length);
        public void to_string(char strings[25]);
    }
    [CCode(cheader_filename = "bits/types.h", cname = "timeval")]
    public struct TimeVal
    {
        ulong tv_sec;		/* Seconds.  */
        ulong tv_usec;	/* Microseconds.  */
    }
    [CCode(cname = "bson_type_t", cprefix = "BSON_TYPE_")]
    public enum BsonType {
        EOD,
        DOUBLE,
        UTF8,
        DOCUMENT,
        ARRAY,
        BINARY,
        UNDEFINED,
        OID,
        BOOL,
        DATE_TIME,
        NULL,
        REGEX,
        DBPOINTER,
        CODE,
        SYMBOL,
        CODEWSCOPE,
        INT32,
        TIMESTAMP,
        INT64,
        DECIMAL128,
        MAXKEY,
        MINKEY
    }
    public struct TimeStamp {
        public uint32 timestamp;
        public uint32 increment;
    }
    public struct Utf8 {
        public string str;
        public uint32 len;
    }
    public struct Document {
        public uint32 data_len;
        public uint8[] data;
    }
    public struct Vinary : Document {
        public BsonSubType subtype;
    }
    public struct Regex {
        public string regex;
        public string options;
    }
    public struct DbPointer {
        public string collection;
        public uint32 collection_len;
        public BsonObjectId oid;
    }
    public struct Code {
        public uint32 code_len;
        public string code;
    }
    public struct CodeWithScope {
        public uint32 code_len;
        public string code;
        public uint32 scope_len;
        public uint8[] scope_data;
    }
    public struct Symbol {
        public uint32 len;
        public string symbol;
    }

    [CCode(cname = "_bson_value_t", cprefix = "bson_value_", destroy_function = "bson_value_destroy")]
    public struct BsonValue {
        BsonType value_type;
        [CCode(cname = "value.v_oid")] public BsonObjectId object_id_value;
        [CCode(cname = "value.v_int64")] public int64 int64_value;
        [CCode(cname = "value.v_int32")] public int32 int32_value;
        [CCode(cname = "value.v_int8")] public int8 int8_value;
        [CCode(cname = "value.v_double")] public double double_value;
        [CCode(cname = "value.v_bool")] public bool bool_value;
        [CCode(cname = "value.v_datetime")] public uint64 datetime_value;
        [CCode(cname = "value.v_timestamp")] public TimeStamp timestamp_value;
        [CCode(cname = "value.v_utf8")] public Utf8 utf8_value;
        [CCode(cname = "value.v_doc")] public Document document_value;
        [CCode(cname = "value.v_binary")] public Document binary_value;
        [CCode(cname = "value.v_regex")] public Regex regex_value;
        [CCode(cname = "value.v_dbpointer")] public DbPointer db_pointer_value;
        [CCode(cname = "value.v_code")] public Code code_value;
        [CCode(cname = "value.v_codewscope")]  public CodeWithScope code_with_scope_value;
        [CCode(cname = "value.v_symbol")] public Symbol symbol_value;
        public void copy(out BsonValue target);
    }
    [CCode(cname = "va_list")]
    public struct VaList{}

    [CCode(cname = "bson_validate_flags_t", cprefix = "BSON_VALIDATE_")]
    public enum ValidateFlags {
        NONE,
        UTF8,
        DOLLAR_KEYS,
        DOT_KEYS,
        UTF8_ALLOW_NULL,
        EMPTY_KEYS,
    }

    [Compact]
    [CCode(cname = "bson_t", cprefix="bson_", free_function = "bson_destroy")]
    public class Bson {
        [CCode(cname = "bson_new")]
        public Bson();
        //  [CCode(cname = "bson_new_from_buffer", simple_generics = true)]
        //  public static Bson from_buffer<T, CONTEXT>(uint8[] buffer, BsonReallocation<T> reallocation, CONTEXT context);
        [CCode(cname = "bson_new_from_json")]
        public static Bson from_json(uint8[] json, out BsonError error = null);
        [CCode(cname = "bson_new_from_data")]
        public static Bson from_data(uint8[] bson);
        public uint32 flags;
        [CCode(cname = "len")]
        public uint32 length;
        public uint8 padding[128];
        public void init();
        public void reinit();
        public string as_json(out ulong? length = null);
        public bool append_array(string key, int key_length = -1, Bson array);
        public bool append_array_begin(string key, int key_length = -1, Bson child);
        public bool append_array_end(string key, int key_length = -1, Bson child);
        public bool append_binary(string key, int key_length, BsonSubType type, uint8[] binary);
        public bool append_bool(string key, int key_length = -1, bool value);
        public bool append_code(string key, int key_length = -1, string javascript);
        public bool append_code_with_scope(string key, int key_length = -1, string javascript, Bson scope);
        public bool append_date_time(string key, int key_length = -1, uint64 value);
        public bool append_decimal128(string key, int key_length = -1, BsonDecimal128 value);
        public bool append_document(string key, int key_length = -1, Bson document);
        public bool append_document_begin(string key, int key_length = -1, Bson document);
        public bool append_document_end(string key, int key_length = -1, Bson document);
        public bool append_double(string key, int key_length = -1, double value);
        public bool append_int32(string key, int key_length = -1, int32 value);
        public bool append_int64(string key, int key_length = -1, int64 value);
        public bool append_maxkey(string key, int key_length = -1);
        public bool append_minkey(string key, int key_length = -1);
        public bool append_now_utc(string key, int key_length = -1);
        public bool append_null(string key, int key_length = -1);
        public bool append_oid(string key, int key_length = -1, BsonObjectId oid);
        public bool append_regex(string key, int key_length = -1, string regex, string options);
        public bool append_regex_w_len(string key, int key_length = -1, string regex, int regex_length, string options);
        public bool append_symbol(string key, int key_length = -1, string symbol, int symbol_length = -1);
        [CCode(cname = "bson_append_time_t")]
        public bool append_time(string key, int key_length = -1, long time);
        public bool append_timestamp(string key, int key_length = -1, uint32 timestamp, uint32 increment);
        public bool append_timeval(string key, int key_length = -1, TimeVal time);
        public bool append_undefined(string key, int key_length = -1);
        public bool append_utf8(string key, int key_length = -1, string text, int text_length = -1);
        public bool append_value(string key, int key_length = -1, BsonValue value);
        public string array_as_json(int? length = null);
        public string as_relaxed_extended_json(int? length = null);
        public int compare(Bson other);
        public bool concat(Bson source);
        public Bson copy();
        public bool copy_to(Bson target);
        public void copy_to_excluding_noinit_va(Bson target, string first_exclude, VaList args);
        public int32 count_keys();
        public static uint8[] destroy_with_steal(Bson bson, bool steal, out uint32 length);
        public int equal(Bson other);
        [CCode(cname = "bson_get_data")]
        public uint8 *get_data();
        private GLib.ByteArray _get_buffer(uint8 *offset) {
            var bytes = new uint8[this.length];
            for (var a = 0; a < bytes.length; a++) {
                bytes[a] = *(offset++);
            }
            return new GLib.ByteArray.take(bytes);
        }
        public GLib.ByteArray get_data_to_bytes() {
            return this._get_buffer(this.get_data());
        }
        public bool init_static(uint8[] data);
        public uint8 *reserve_buffer(uint32 size);
        public GLib.ByteArray reserve_buffer_to_bytes(uint32 size) {
            return this._get_buffer(this.reserve_buffer(size));
        }
        public Bson sized_new(uint64 length);
        public bool steal(Bson source);
        bool validate(ValidateFlags flags, out uint64 offset);
        bool validate_with_error(ValidateFlags flags, out BsonError error);
    }
}
